#ifndef PRYN_WINAMP_H
#define PRYN_WINAMP_H

#if __cplusplus
extern "C" {
#endif

typedef struct WinampInModule WinampInModule;

#define WM_WA_IPC (WM_USER)

#define IPC_GET_API_SERVICE 3025 /// (0xBD1) Return the WinampAPIService*.

#if 0
// note: exported symbol is now winampGetInModule2.

#define IN_UNICODE 0x0F000000

#ifdef UNICODE_INPUT_PLUGIN
#define in_char wchar_t
#define IN_VER (IN_UNICODE | 0x100)
#else
#define in_char char
#define IN_VER 0x100
#endif

#define IN_MODULE_FLAG_USES_OUTPUT_PLUGIN 1
// By default, Winamp assumes that your input plugin wants to use Winamp's EQ, and doesn't do replay gain
// if you handle any of these yourself (EQ, Replay Gain adjustments), then set these flags accordingly
#define IN_MODULE_FLAG_EQ 2 // set this if you do your own EQ
#define IN_MODULE_FLAG_REPLAYGAIN 8 // set this if you adjusted volume for replay gain
																		// for tracks with no replay gain metadata, you should clear this flag 
																		// UNLESS you handle "non_replaygain" gain adjustment yourself
#define IN_MODULE_FLAG_REPLAYGAIN_PREAMP 16 // use this if you queried for the replay gain preamp parameter and used it 
																						// this parameter is new to 5.54

// return values from the winampUninstallPlugin(HINSTANCE hdll, HWND parent, int param)
// which determine if we can uninstall the plugin immediately or on winamp restart
//
// uninstall support was added from 5.0+ and uninstall now support from 5.5+
// it is down to you to ensure that if uninstall now is returned that it will not cause a crash
// (ie don't use if you've been subclassing the main window)
#define IN_PLUGIN_UNINSTALL_NOW    0x1
#define IN_PLUGIN_UNINSTALL_REBOOT 0x0
#endif

/// An input Winamp module.
struct WinampInModule
{
	int version; /// Module revision.
	char *description; /// Description of the module.
	HWND mainWindow; /// Main window; filled in by caller.
	HINSTANCE instance; /// DLL instance; filled in by caller.
	char *extensions; /// List of supported extensions, each terminated by NUL, with the list terminated with an empty string.
	int seekable; /// Whether the stream is seekable.
	int usesOutputPlugin; /// Whether this plugin uses the output plugins.
	void (*config) (HWND parent); /// Present a configuration dialog.
	void (*about) (HWND parent); /// Present an about dialog.
	void (*init) (); /// Initialise the plugin.
	void (*quit) (); /// Deinitialise the plugin.
	void (*getFileInfo) (const char *file, char *title, int *lengthInMilliseconds);
	int (*infoBox) (const char *file, HWND parent);
};

#if 0
#define GETFILEINFO_TITLE_LENGTH 2048 
	void (*GetFileInfo)(const in_char *file, in_char *title, int *length_in_ms); // if file == NULL, current playing is used

#define INFOBOX_EDITED 0
#define INFOBOX_UNCHANGED 1
	int (*InfoBox)(const in_char *file, HWND hwndParent);
	
	int (*IsOurFile)(const in_char *fn);	// called before extension checks, to allow detection of mms://, etc
	// playback stuff
	int (*Play)(const in_char *fn);		// return zero on success, -1 on file-not-found, some other value on other (stopping winamp) error
	void (*Pause)();			// pause stream
	void (*UnPause)();			// unpause stream
	int (*IsPaused)();			// ispaused? return 1 if paused, 0 if not
	void (*Stop)();				// stop (unload) stream

	// time stuff
	int (*GetLength)();			// get length in ms
	int (*GetOutputTime)();		// returns current output time in ms. (usually returns outMod->GetOutputTime()
	void (*SetOutputTime)(int time_in_ms);	// seeks to point in stream (in ms). Usually you signal your thread to seek, which seeks and calls outMod->Flush()..

	// volume stuff
	void (*SetVolume)(int volume);	// from 0 to 255.. usually just call outMod->SetVolume
	void (*SetPan)(int pan);	// from -127 to 127.. usually just call outMod->SetPan
	
	// in-window builtin vis stuff

	void (*SAVSAInit)(int maxlatency_in_ms, int srate);		// call once in Play(). maxlatency_in_ms should be the value returned from outMod->Open()
	// call after opening audio device with max latency in ms and samplerate
	void (*SAVSADeInit)();	// call in Stop()


	// simple vis supplying mode
	void (*SAAddPCMData)(void *PCMData, int nch, int bps, int timestamp); 
											// sets the spec data directly from PCM data
											// quick and easy way to get vis working :)
											// needs at least 576 samples :)

	// advanced vis supplying mode, only use if you're cool. Use SAAddPCMData for most stuff.
	int (*SAGetMode)();		// gets csa (the current type (4=ws,2=osc,1=spec))
							// use when calling SAAdd()
	int (*SAAdd)(void *data, int timestamp, int csa); // sets the spec data, filled in by winamp


	// vis stuff (plug-in)
	// simple vis supplying mode
	void (*VSAAddPCMData)(void *PCMData, int nch, int bps, int timestamp); // sets the vis data directly from PCM data
											// quick and easy way to get vis working :)
											// needs at least 576 samples :)

	// advanced vis supplying mode, only use if you're cool. Use VSAAddPCMData for most stuff.
	int (*VSAGetMode)(int *specNch, int *waveNch); // use to figure out what to give to VSAAdd
	int (*VSAAdd)(void *data, int timestamp); // filled in by winamp, called by plug-in


	// call this in Play() to tell the vis plug-ins the current output params. 
	void (*VSASetInfo)(int srate, int nch); // <-- Correct (benski, dec 2005).. old declaration had the params backwards


	// dsp plug-in processing: 
	// (filled in by winamp, calld by input plug)

	// returns 1 if active (which means that the number of samples returned by dsp_dosamples
	// could be greater than went in.. Use it to estimate if you'll have enough room in the
	// output buffer
	int (*dsp_isactive)(); 

	// returns number of samples to output. This can be as much as twice numsamples. 
	// be sure to allocate enough buffer for samples, then.
	int (*dsp_dosamples)(short int *samples, int numsamples, int bps, int nch, int srate);


	// eq stuff
	void (*EQSet)(int on, char data[10], int preamp); // 0-64 each, 31 is +0, 0 is +12, 63 is -12. Do nothing to ignore.

	// info setting (filled in by winamp)
	void (*SetInfo)(int bitrate, int srate, int stereo, int synched); // if -1, changes ignored? :)

	Out_Module *outMod; // filled in by winamp, optionally used :)
} In_Module;
#endif

#if __cplusplus
}
#endif

class WinampDispatchable
{
public:
	virtual int _dispatch (int msg, void *retval, void **params = 0, int nparam = 0) = 0;
	
protected:
	template <class RETURN_TYPE>
	RETURN_TYPE _call (int msg, RETURN_TYPE defval)
	{
		RETURN_TYPE retval;
		if  (_dispatch (msg, &retval)) return retval;
		return defval;
	}
	
	template <class RETURN_TYPE, class PARAM1>
	RETURN_TYPE _call (int msg, RETURN_TYPE defval, PARAM1 param1)
	{
		void *params [1] = { &param1 };
		RETURN_TYPE retval;
		if  (_dispatch (msg, &retval, params, 1)) return retval;
		return defval;
	}
	
	template <class RETURN_TYPE, class PARAM1, class PARAM2>
	RETURN_TYPE _call (int msg, RETURN_TYPE defval, PARAM1 param1, PARAM2 param2)
	{
		void *params [2] = { &param1, &param2 };
		RETURN_TYPE retval;
		if  (_dispatch (msg, &retval, params, 2)) return retval;
		return defval;
	}
	
	template <class RETURN_TYPE, class PARAM1, class PARAM2, class PARAM3>
	RETURN_TYPE _call (int msg, RETURN_TYPE defval, PARAM1 param1, PARAM2 param2, PARAM3 param3)
	{
		void *params [3] = { &param1, &param2, &param3 };
		RETURN_TYPE retval;
		if  (_dispatch (msg, &retval, params, 3)) return retval;
		return defval;
	}
	
	template <class RETURN_TYPE, class PARAM1, class PARAM2, class PARAM3, class PARAM4>
	RETURN_TYPE _call (int msg, RETURN_TYPE defval, PARAM1 param1, PARAM2 param2, PARAM3 param3, PARAM4 param4)
	{
		void *params [4] = { &param1, &param2, &param3, &param4 };
		RETURN_TYPE retval;
		if  (_dispatch (msg, &retval, params, 4)) return retval;
		return defval;
	}
	
	template <class RETURN_TYPE, class PARAM1, class PARAM2, class PARAM3, class PARAM4, class PARAM5>
	RETURN_TYPE _call (int msg, RETURN_TYPE defval, PARAM1 param1, PARAM2 param2, PARAM3 param3, PARAM4 param4, PARAM5 param5)
	{
		void *params [5] = { &param1, &param2, &param3, &param4, &param5 };
		RETURN_TYPE retval;
		if  (_dispatch (msg, &retval, params, 5)) return retval;
		return defval;
	}
	
	template <class RETURN_TYPE, class PARAM1, class PARAM2, class PARAM3, class PARAM4, class PARAM5, class PARAM6>
	RETURN_TYPE _call (int msg, RETURN_TYPE defval, PARAM1 param1, PARAM2 param2, PARAM3 param3, PARAM4 param4, PARAM5 param5, PARAM6 param6)
	{
		void *params [6] = { &param1, &param2, &param3, &param4, &param5, &param6 };
		RETURN_TYPE retval;
		if  (_dispatch (msg, &retval, params, 6)) return retval;
		return defval;
	}
	
	template <class RETURN_TYPE, class PARAM1, class PARAM2, class PARAM3, class PARAM4, class PARAM5, class PARAM6, class PARAM7>
	RETURN_TYPE _call (int msg, RETURN_TYPE defval, PARAM1 param1, PARAM2 param2, PARAM3 param3, PARAM4 param4, PARAM5 param5, PARAM6 param6, PARAM7 param7)
	{
		void *params [7] = { &param1, &param2, &param3, &param4, &param5, &param6, &param7 };
		RETURN_TYPE retval;
		if  (_dispatch (msg, &retval, params, 7)) return retval;
		return defval;
	}
	
	template <class RETURN_TYPE, class PARAM1, class PARAM2, class PARAM3, class PARAM4, class PARAM5, class PARAM6, class PARAM7, class PARAM8>
	RETURN_TYPE _call (int msg, RETURN_TYPE defval, PARAM1 param1, PARAM2 param2, PARAM3 param3, PARAM4 param4, PARAM5 param5, PARAM6 param6, PARAM7 param7, PARAM8 param8)
	{
		void *params [8] = { &param1, &param2, &param3, &param4, &param5, &param6, &param7, &param8 };
		RETURN_TYPE retval;
		if  (_dispatch (msg, &retval, params, 8)) return retval;
		return defval;
	}
	
	template <class RETURN_TYPE, class PARAM1, class PARAM2, class PARAM3, class PARAM4, class PARAM5, class PARAM6, class PARAM7, class PARAM8, class PARAM9>
	RETURN_TYPE _call (int msg, RETURN_TYPE defval, PARAM1 param1, PARAM2 param2, PARAM3 param3, PARAM4 param4, PARAM5 param5, PARAM6 param6, PARAM7 param7, PARAM8 param8, PARAM9 param9)
	{
		void *params [9] = { &param1, &param2, &param3, &param4, &param5, &param6, &param7, &param8, &param9 };
		RETURN_TYPE retval;
		if  (_dispatch (msg, &retval, params, 9)) return retval;
		return defval;
	}
	
	template <class RETURN_TYPE, class PARAM1, class PARAM2, class PARAM3, class PARAM4, class PARAM5, class PARAM6, class PARAM7, class PARAM8, class PARAM9, class PARAM10>
	RETURN_TYPE _call (int msg, RETURN_TYPE defval, PARAM1 param1, PARAM2 param2, PARAM3 param3, PARAM4 param4, PARAM5 param5, PARAM6 param6, PARAM7 param7, PARAM8 param8, PARAM9 param9, PARAM10 param10)
	{
		void *params [10] = { &param1, &param2, &param3, &param4, &param5, &param6, &param7, &param8, &param9, &param10 };
		RETURN_TYPE retval;
		if  (_dispatch (msg, &retval, params, 10)) return retval;
		return defval;
	}	
};

struct WinampServiceFactory;

class WinampAPIService : public WinampDispatchable
{
public:
	WinampAPIService () {}
	~WinampAPIService () {}
	
public:
	virtual int _dispatch (int msg, void *retval, void **params = 0, int nparam = 0);
	
	int service_register (WinampServiceFactory *svc);
	int service_deregister (WinampServiceFactory *svc);
	size_t service_getNumServices (FOURCC svc_type);
	WinampServiceFactory *service_enumService (FOURCC svc_type, size_t n);
	WinampServiceFactory *service_getServiceByGuid (GUID guid);
	int service_lock (WinampServiceFactory *owner, void *svcptr);
	int service_clientLock (void *svcptr);
	int service_release (void *svcptr);
	const char *service_getTypeName (FOURCC svc_type);
	#ifdef WASABI_COMPILE_COMPONENTS
	GUID service_getOwningComponent (void *svcptr);
	GUID service_getLockingComponent (void *svcptr);
	#endif // WASABI_COMPILE_COMPONENTS
	int service_unlock (void *svcptr);
	int service_isvalid (FOURCC svctype, WinampServiceFactory *service);
		// removes "me" from the services list and finds a second service with the same GUID and puts it in the same position
		// this is used by the lazy loader service factory - you shouldn't need it for any other purposes.
		// returns 0 if compaction actually happened
	int service_compactDuplicates (WinampServiceFactory *me);
	
protected:
	enum
	{
		API_SERVICE_SERVICE_REGISTER = 10,
		API_SERVICE_SERVICE_DEREGISTER = 20,
		API_SERVICE_SERVICE_GETNUMSERVICES = 30,
		API_SERVICE_SERVICE_ENUMSERVICE = 40,
		API_SERVICE_SERVICE_GETSERVICEBYGUID = 50,
		API_SERVICE_SERVICE_LOCK = 60,
		API_SERVICE_SERVICE_CLIENTLOCK = 70,
		API_SERVICE_SERVICE_RELEASE = 80,
		API_SERVICE_SERVICE_GETTYPENAME = 90,
		#ifdef WASABI_COMPILE_COMPONENTS
		API_SERVICE_SERVICE_GETOWNINGCOMPONENT = 100,
		API_SERVICE_SERVICE_GETLOCKINGCOMPONENT = 110,
		#endif // WASABI_COMPILE_COMPONENTS
		API_SERVICE_SERVICE_UNLOCK = 120,
		API_SERVICE_ISVALID = 130,
		API_SERVICE_COMPACT_DUPLICATES = 140,
	};
};

inline int WinampAPIService::service_register (WinampServiceFactory *svc) {
  int __retval = _call (API_SERVICE_SERVICE_REGISTER,  (int)0, svc);
  return __retval;
}

inline int WinampAPIService::service_deregister (WinampServiceFactory *svc) {
  int __retval = _call (API_SERVICE_SERVICE_DEREGISTER,  (int)0, svc);
  return __retval;
}

inline size_t WinampAPIService::service_getNumServices (FOURCC svc_type) {
  int __retval = _call (API_SERVICE_SERVICE_GETNUMSERVICES,  (int)0, svc_type);
  return __retval;
}

inline WinampServiceFactory *WinampAPIService::service_enumService (FOURCC svc_type, size_t n) {
  WinampServiceFactory *__retval = _call (API_SERVICE_SERVICE_ENUMSERVICE,  (WinampServiceFactory *)0, svc_type, n);
  return __retval;
}

inline WinampServiceFactory *WinampAPIService::service_getServiceByGuid (GUID guid) {
  WinampServiceFactory *__retval = _call (API_SERVICE_SERVICE_GETSERVICEBYGUID,  (WinampServiceFactory *)0, guid);
  return __retval;
}

inline int WinampAPIService::service_lock (WinampServiceFactory *owner, void *svcptr) {
  int __retval = _call (API_SERVICE_SERVICE_LOCK,  (int)0, owner, svcptr);
  return __retval;
}

inline int WinampAPIService::service_clientLock (void *svcptr) {
  int __retval = _call (API_SERVICE_SERVICE_CLIENTLOCK,  (int)0, svcptr);
  return __retval;
}

inline int WinampAPIService::service_release (void *svcptr) {
  int __retval = _call (API_SERVICE_SERVICE_RELEASE,  (int)0, svcptr);
  return __retval;
}

inline const char *WinampAPIService::service_getTypeName (FOURCC svc_type) {
  const char *__retval = _call (API_SERVICE_SERVICE_GETTYPENAME,  (const char *)0, svc_type);
  return __retval;
}

#ifdef WASABI_COMPILE_COMPONENTS
inline GUID WinampAPIService::service_getOwningComponent (void *svcptr) {
  GUID __retval = _call (API_SERVICE_SERVICE_GETOWNINGCOMPONENT, INVALID_GUID, svcptr);
  return __retval;
}

inline GUID WinampAPIService::service_getLockingComponent (void *svcptr) {
  GUID __retval = _call (API_SERVICE_SERVICE_GETLOCKINGCOMPONENT, INVALID_GUID, svcptr);
  return __retval;
}

#endif // WASABI_COMPILE_COMPONENTS
inline int WinampAPIService::service_unlock (void *svcptr) {
  int __retval = _call (API_SERVICE_SERVICE_UNLOCK,  (int)0, svcptr);
  return __retval;
}

inline int WinampAPIService::service_isvalid (FOURCC svctype, WinampServiceFactory *service) {
  int __retval = _call (API_SERVICE_ISVALID,  (int)0, svctype, service);
  return __retval;
}

inline int WinampAPIService::service_compactDuplicates (WinampServiceFactory *me)
{
	return _call (API_SERVICE_COMPACT_DUPLICATES,  (int)1, me);
}

#endif /* PRYN_WINAMP_H */
